<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>The source code</title>
  <link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
  <script type="text/javascript" src="../resources/prettify/prettify.js"></script>
  <style type="text/css">
    .highlight { display: block; background-color: #ddd; }
  </style>
  <script type="text/javascript">
    function highlight() {
      document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
    }
  </script>
</head>
<body onload="prettyPrint(); highlight();">
  <pre class="prettyprint lang-js">var curve = require(&quot;../utils/curve_util&quot;);

var vectorUtil = require(&quot;../utils/vector_util&quot;);

var bbox = require(&quot;../utils/bbox_util&quot;);

var BoundingRect = require(&quot;./BoundingRect&quot;);

var _config = require(&quot;../config&quot;);

var dpr = _config.devicePixelRatio;

var _constants = require(&quot;../utils/constants&quot;);

var mathMin = _constants.mathMin;
var mathMax = _constants.mathMax;
var mathCos = _constants.mathCos;
var mathSin = _constants.mathSin;
var mathSqrt = _constants.mathSqrt;
var mathAbs = _constants.mathAbs;
// TODO: getTotalLength, getPointAtLength

<span id='qrenderer-core-PathProxy'>/**
</span> * @class qrenderer.core.PathProxy
 * 
 * Path 代理，可以在`buildPath`中用于替代`ctx`, 会保存每个path操作的命令到pathCommands属性中
 * 可以用于 isInsidePath 判断以及获取boundingRect
 * 
 * @author Yi Shen (http://www.github.com/pissang)
 * @docauthor 大漠穷秋 &lt;damoqiongqiu@126.com&gt;
 */
var CMD = {
  M: 1,
  L: 2,
  C: 3,
  Q: 4,
  A: 5,
  Z: 6,
  R: 7
};
var min1 = [];
var max1 = [];
var min2 = [];
var max2 = [];
var hasTypedArray = typeof Float32Array !== &#39;undefined&#39;;
<span id='qrenderer-core-PathProxy-method-constructor'>/**
</span> * @method constructor PathProxy
 */

var PathProxy = function PathProxy(notSaveData) {
  this._saveData = !(notSaveData || false);

  if (this._saveData) {
<span id='qrenderer-core-PathProxy-property-data'>    /**
</span>     * Path data. Stored as flat array
     * @property {Array&lt;Object&gt;}
     */
    this.data = [];
  }

  this._ctx = null;
};
<span id='qrenderer-core-PathProxy-method-prototype'>/**
</span> * 快速计算Path包围盒（并不是最小包围盒）
 * @return {Object}
 */


PathProxy.prototype = {
  constructor: PathProxy,
  _xi: 0,
  _yi: 0,
  _x0: 0,
  _y0: 0,
  // Unit x, Unit y. Provide for avoiding drawing that too short line segment
  _ux: 0,
  _uy: 0,
  _len: 0,
  _lineDash: null,
  _dashOffset: 0,
  _dashIdx: 0,
  _dashSum: 0,

<span id='qrenderer-core-PathProxy-method-setScale'>  /**
</span>   * @readonly
   */
  setScale: function setScale(sx, sy, segmentIgnoreThreshold) {
    // Compat. Previously there is no segmentIgnoreThreshold.
    segmentIgnoreThreshold = segmentIgnoreThreshold || 0;
    this._ux = mathAbs(segmentIgnoreThreshold / dpr / sx) || 0;
    this._uy = mathAbs(segmentIgnoreThreshold / dpr / sy) || 0;
  },
  getContext: function getContext() {
    return this._ctx;
  },

<span id='qrenderer-core-PathProxy-method-beginPath'>  /**
</span>   * @method beginPath
   * @param  {CanvasRenderingContext2D} ctx
   * @return {PathProxy}
   */
  beginPath: function beginPath(ctx) {
    this._ctx = ctx;
    ctx &amp;&amp; ctx.beginPath();
    ctx &amp;&amp; (this.dpr = ctx.dpr); // Reset

    if (this._saveData) {
      this._len = 0;
    }

    if (this._lineDash) {
      this._lineDash = null;
      this._dashOffset = 0;
    }

    return this;
  },

<span id='qrenderer-core-PathProxy-method-moveTo'>  /**
</span>   * @method moveTo
   * @param  {Number} x
   * @param  {Number} y
   * @return {PathProxy}
   */
  moveTo: function moveTo(x, y) {
    this.addData(CMD.M, x, y);
    this._ctx &amp;&amp; this._ctx.moveTo(x, y); // x0, y0, xi, yi 是记录在 _dashedXXXXTo 方法中使用
    // xi, yi 记录当前点, x0, y0 在 closePath 的时候回到起始点。
    // 有可能在 beginPath 之后直接调用 lineTo，这时候 x0, y0 需要
    // 在 lineTo 方法中记录，这里先不考虑这种情况，dashed line 也只在 IE10- 中不支持

    this._x0 = x;
    this._y0 = y;
    this._xi = x;
    this._yi = y;
    return this;
  },

<span id='qrenderer-core-PathProxy-method-lineTo'>  /**
</span>   * @method lineTo
   * @param  {Number} x
   * @param  {Number} y
   * @return {PathProxy}
   */
  lineTo: function lineTo(x, y) {
    var exceedUnit = mathAbs(x - this._xi) &gt; this._ux || mathAbs(y - this._yi) &gt; this._uy // Force draw the first segment
    || this._len &lt; 5;
    this.addData(CMD.L, x, y);

    if (this._ctx &amp;&amp; exceedUnit) {
      this._needsDash() ? this._dashedLineTo(x, y) : this._ctx.lineTo(x, y);
    }

    if (exceedUnit) {
      this._xi = x;
      this._yi = y;
    }

    return this;
  },

<span id='qrenderer-core-PathProxy-method-bezierCurveTo'>  /**
</span>   * @method bezierCurveTo
   * @param  {Number} x1
   * @param  {Number} y1
   * @param  {Number} x2
   * @param  {Number} y2
   * @param  {Number} x3
   * @param  {Number} y3
   * @return {PathProxy}
   */
  bezierCurveTo: function bezierCurveTo(x1, y1, x2, y2, x3, y3) {
    this.addData(CMD.C, x1, y1, x2, y2, x3, y3);

    if (this._ctx) {
      this._needsDash() ? this._dashedBezierTo(x1, y1, x2, y2, x3, y3) : this._ctx.bezierCurveTo(x1, y1, x2, y2, x3, y3);
    }

    this._xi = x3;
    this._yi = y3;
    return this;
  },

<span id='qrenderer-core-PathProxy-method-quadraticCurveTo'>  /**
</span>   * @method quadraticCurveTo
   * @param  {Number} x1
   * @param  {Number} y1
   * @param  {Number} x2
   * @param  {Number} y2
   * @return {PathProxy}
   */
  quadraticCurveTo: function quadraticCurveTo(x1, y1, x2, y2) {
    this.addData(CMD.Q, x1, y1, x2, y2);

    if (this._ctx) {
      this._needsDash() ? this._dashedQuadraticTo(x1, y1, x2, y2) : this._ctx.quadraticCurveTo(x1, y1, x2, y2);
    }

    this._xi = x2;
    this._yi = y2;
    return this;
  },

<span id='qrenderer-core-PathProxy-method-arc'>  /**
</span>   * @method arc
   * @param  {Number} cx
   * @param  {Number} cy
   * @param  {Number} r
   * @param  {Number} startAngle
   * @param  {Number} endAngle
   * @param  {Boolean} anticlockwise
   * @return {PathProxy}
   */
  arc: function arc(cx, cy, r, startAngle, endAngle, anticlockwise) {
    this.addData(CMD.A, cx, cy, r, r, startAngle, endAngle - startAngle, 0, anticlockwise ? 0 : 1);
    this._ctx &amp;&amp; this._ctx.arc(cx, cy, r, startAngle, endAngle, anticlockwise);
    this._xi = mathCos(endAngle) * r + cx;
    this._yi = mathSin(endAngle) * r + cy;
    return this;
  },
  // TODO
  arcTo: function arcTo(x1, y1, x2, y2, radius) {
    if (this._ctx) {
      this._ctx.arcTo(x1, y1, x2, y2, radius);
    }

    return this;
  },
  // TODO
  rect: function rect(x, y, w, h) {
    this._ctx &amp;&amp; this._ctx.rect(x, y, w, h);
    this.addData(CMD.R, x, y, w, h);
    return this;
  },

<span id='qrenderer-core-PathProxy-method-closePath'>  /**
</span>   * @method closePath
   * @return {PathProxy}
   */
  closePath: function closePath() {
    this.addData(CMD.Z);
    var ctx = this._ctx;
    var x0 = this._x0;
    var y0 = this._y0;

    if (ctx) {
      this._needsDash() &amp;&amp; this._dashedLineTo(x0, y0);
      ctx.closePath();
    }

    this._xi = x0;
    this._yi = y0;
    return this;
  },

<span id='qrenderer-core-PathProxy-method-fill'>  /**
</span>   * @method fill
   * Context 从外部传入，因为有可能是 rebuildPath 完之后再 fill。
   * stroke 同样
   * @param {CanvasRenderingContext2D} ctx
   * @return {PathProxy}
   */
  fill: function fill(ctx) {
    ctx &amp;&amp; ctx.fill();
    this.toStatic();
  },

<span id='qrenderer-core-PathProxy-method-stroke'>  /**
</span>   * @method stroke
   * @param {CanvasRenderingContext2D} ctx
   * @return {PathProxy}
   */
  stroke: function stroke(ctx) {
    ctx &amp;&amp; ctx.stroke();
    this.toStatic();
  },

<span id='qrenderer-core-PathProxy-method-setLineDash'>  /**
</span>   * @method setLineDash
   * 必须在其它绘制命令前调用
   * Must be invoked before all other path drawing methods
   * @return {PathProxy}
   */
  setLineDash: function setLineDash(lineDash) {
    if (lineDash instanceof Array) {
      this._lineDash = lineDash;
      this._dashIdx = 0;
      var lineDashSum = 0;

      for (var i = 0; i &lt; lineDash.length; i++) {
        lineDashSum += lineDash[i];
      }

      this._dashSum = lineDashSum;
    }

    return this;
  },

<span id='qrenderer-core-PathProxy-method-setLineDashOffset'>  /**
</span>   * @method setLineDashOffset
   * 必须在其它绘制命令前调用
   * Must be invoked before all other path drawing methods
   * @return {PathProxy}
   */
  setLineDashOffset: function setLineDashOffset(offset) {
    this._dashOffset = offset;
    return this;
  },

<span id='qrenderer-core-PathProxy-method-len'>  /**
</span>   * @method len
   * @return {Boolean}
   */
  len: function len() {
    return this._len;
  },

<span id='qrenderer-core-PathProxy-method-setData'>  /**
</span>   * @method setData
   * 直接设置 Path 数据
   */
  setData: function setData(data) {
    var len = data.length;

    if (!(this.data &amp;&amp; this.data.length === len) &amp;&amp; hasTypedArray) {
      this.data = new Float32Array(len);
    }

    for (var i = 0; i &lt; len; i++) {
      this.data[i] = data[i];
    }

    this._len = len;
  },

<span id='qrenderer-core-PathProxy-method-appendPath'>  /**
</span>   * @method appendPath
   * 添加子路径
   * @param {PathProxy|Array.&lt;PathProxy&gt;} path
   */
  appendPath: function appendPath(path) {
    if (!(path instanceof Array)) {
      path = [path];
    }

    var len = path.length;
    var appendSize = 0;
    var offset = this._len;

    for (var i = 0; i &lt; len; i++) {
      appendSize += path[i].len();
    }

    if (hasTypedArray &amp;&amp; this.data instanceof Float32Array) {
      this.data = new Float32Array(offset + appendSize);
    }

    for (var _i = 0; _i &lt; len; _i++) {
      var appendPathData = path[_i].data;

      for (var k = 0; k &lt; appendPathData.length; k++) {
        this.data[offset++] = appendPathData[k];
      }
    }

    this._len = offset;
  },

<span id='qrenderer-core-PathProxy-method-addData'>  /**
</span>   * @method addData
   * 填充 Path 数据。
   * 尽量复用而不申明新的数组。大部分图形重绘的指令数据长度都是不变的。
   */
  addData: function addData(cmd) {
    if (!this._saveData) {
      return;
    }

    var data = this.data;

    if (this._len + arguments.length &gt; data.length) {
      // 因为之前的数组已经转换成静态的 Float32Array
      // 所以不够用时需要扩展一个新的动态数组
      this._expandData();

      data = this.data;
    }

    for (var i = 0; i &lt; arguments.length; i++) {
      data[this._len++] = arguments[i];
    }

    this._prevCmd = cmd;
  },
  _expandData: function _expandData() {
    // Only if data is Float32Array
    if (!(this.data instanceof Array)) {
      var newData = [];

      for (var i = 0; i &lt; this._len; i++) {
        newData[i] = this.data[i];
      }

      this.data = newData;
    }
  },

<span id='qrenderer-core-PathProxy-method-_needsDash'>  /**
</span>   * If needs js implemented dashed line
   * @return {Boolean}
   * @private
   */
  _needsDash: function _needsDash() {
    return this._lineDash;
  },
  _dashedLineTo: function _dashedLineTo(x1, y1) {
    var dashSum = this._dashSum;
    var offset = this._dashOffset;
    var lineDash = this._lineDash;
    var ctx = this._ctx;
    var x0 = this._xi;
    var y0 = this._yi;
    var dx = x1 - x0;
    var dy = y1 - y0;
    var dist = mathSqrt(dx * dx + dy * dy);
    var x = x0;
    var y = y0;
    var dash;
    var nDash = lineDash.length;
    var idx;
    dx /= dist;
    dy /= dist;

    if (offset &lt; 0) {
      // Convert to positive offset
      offset = dashSum + offset;
    }

    offset %= dashSum;
    x -= offset * dx;
    y -= offset * dy;

    while (dx &gt; 0 &amp;&amp; x &lt;= x1 || dx &lt; 0 &amp;&amp; x &gt;= x1 || dx === 0 &amp;&amp; (dy &gt; 0 &amp;&amp; y &lt;= y1 || dy &lt; 0 &amp;&amp; y &gt;= y1)) {
      idx = this._dashIdx;
      dash = lineDash[idx];
      x += dx * dash;
      y += dy * dash;
      this._dashIdx = (idx + 1) % nDash; // Skip positive offset

      if (dx &gt; 0 &amp;&amp; x &lt; x0 || dx &lt; 0 &amp;&amp; x &gt; x0 || dy &gt; 0 &amp;&amp; y &lt; y0 || dy &lt; 0 &amp;&amp; y &gt; y0) {
        continue;
      }

      ctx[idx % 2 ? &#39;moveTo&#39; : &#39;lineTo&#39;](dx &gt;= 0 ? mathMin(x, x1) : mathMax(x, x1), dy &gt;= 0 ? mathMin(y, y1) : mathMax(y, y1));
    } // Offset for next lineTo


    dx = x - x1;
    dy = y - y1;
    this._dashOffset = -mathSqrt(dx * dx + dy * dy);
  },
  // Not accurate dashed line to
  _dashedBezierTo: function _dashedBezierTo(x1, y1, x2, y2, x3, y3) {
    var dashSum = this._dashSum;
    var offset = this._dashOffset;
    var lineDash = this._lineDash;
    var ctx = this._ctx;
    var x0 = this._xi;
    var y0 = this._yi;
    var t;
    var dx;
    var dy;
    var cubicAt = curve.cubicAt;
    var bezierLen = 0;
    var idx = this._dashIdx;
    var nDash = lineDash.length;
    var x;
    var y;
    var tmpLen = 0;

    if (offset &lt; 0) {
      // Convert to positive offset
      offset = dashSum + offset;
    }

    offset %= dashSum; // Bezier approx length

    for (t = 0; t &lt; 1; t += 0.1) {
      dx = cubicAt(x0, x1, x2, x3, t + 0.1) - cubicAt(x0, x1, x2, x3, t);
      dy = cubicAt(y0, y1, y2, y3, t + 0.1) - cubicAt(y0, y1, y2, y3, t);
      bezierLen += mathSqrt(dx * dx + dy * dy);
    } // Find idx after add offset


    for (; idx &lt; nDash; idx++) {
      tmpLen += lineDash[idx];

      if (tmpLen &gt; offset) {
        break;
      }
    }

    t = (tmpLen - offset) / bezierLen;

    while (t &lt;= 1) {
      x = cubicAt(x0, x1, x2, x3, t);
      y = cubicAt(y0, y1, y2, y3, t); // Use line to approximate dashed bezier
      // Bad result if dash is long

      idx % 2 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
      t += lineDash[idx] / bezierLen;
      idx = (idx + 1) % nDash;
    } // Finish the last segment and calculate the new offset


    idx % 2 !== 0 &amp;&amp; ctx.lineTo(x3, y3);
    dx = x3 - x;
    dy = y3 - y;
    this._dashOffset = -mathSqrt(dx * dx + dy * dy);
  },
  _dashedQuadraticTo: function _dashedQuadraticTo(x1, y1, x2, y2) {
    // Convert quadratic to cubic using degree elevation
    var x3 = x2;
    var y3 = y2;
    x2 = (x2 + 2 * x1) / 3;
    y2 = (y2 + 2 * y1) / 3;
    x1 = (this._xi + 2 * x1) / 3;
    y1 = (this._yi + 2 * y1) / 3;

    this._dashedBezierTo(x1, y1, x2, y2, x3, y3);
  },

<span id='qrenderer-core-PathProxy-method-toStatic'>  /**
</span>   * 转成静态的 Float32Array 减少堆内存占用
   * Convert dynamic array to static Float32Array
   */
  toStatic: function toStatic() {
    var data = this.data;

    if (data instanceof Array) {
      data.length = this._len;

      if (hasTypedArray) {
        this.data = new Float32Array(data);
      }
    }
  },

<span id='qrenderer-core-PathProxy-method-getBoundingRect'>  /**
</span>   * @method getBoundingRect
   * @return {BoundingRect}
   */
  getBoundingRect: function getBoundingRect() {
    min1[0] = min1[1] = min2[0] = min2[1] = Number.MAX_VALUE;
    max1[0] = max1[1] = max2[0] = max2[1] = -Number.MAX_VALUE;
    var data = this.data;
    var xi = 0;
    var yi = 0;
    var x0 = 0;
    var y0 = 0;
    var i = 0;
    var cx = 0;
    var cy = 0;
    var rx = 0;
    var ry = 0;
    var startAngle = 0;
    var endAngle = 0;
    var anticlockwise = 0;
    var width = 0;
    var height = 0;
    var cmd = 0;

    for (; i &lt; data.length;) {
      cmd = data[i++];

      if (i === 1) {
        // 如果第一个命令是 L, C, Q
        // 则 previous point 同绘制命令的第一个 point
        //
        // 第一个命令为 Arc 的情况下会在后面特殊处理
        xi = data[i];
        yi = data[i + 1];
        x0 = xi;
        y0 = yi;
      }

      switch (cmd) {
        case CMD.M:
          // moveTo 命令重新创建一个新的 subpath, 并且更新新的起点
          // 在 closePath 的时候使用
          x0 = data[i++];
          y0 = data[i++];
          xi = x0;
          yi = y0;
          min2[0] = x0;
          min2[1] = y0;
          max2[0] = x0;
          max2[1] = y0;
          break;

        case CMD.L:
          bbox.fromLine(xi, yi, data[i], data[i + 1], min2, max2);
          xi = data[i++];
          yi = data[i++];
          break;

        case CMD.C:
          bbox.fromCubic(xi, yi, data[i++], data[i++], data[i++], data[i++], data[i], data[i + 1], min2, max2);
          xi = data[i++];
          yi = data[i++];
          break;

        case CMD.Q:
          bbox.fromQuadratic(xi, yi, data[i++], data[i++], data[i], data[i + 1], min2, max2);
          xi = data[i++];
          yi = data[i++];
          break;

        case CMD.A:
          // TODO Arc 判断的开销比较大
          cx = data[i++];
          cy = data[i++];
          rx = data[i++];
          ry = data[i++];
          startAngle = data[i++];
          endAngle = data[i++] + startAngle; // TODO Arc 旋转

          i += 1;
          anticlockwise = 1 - data[i++];

          if (i === 1) {
            // 直接使用 arc 命令
            // 第一个命令起点还未定义
            x0 = mathCos(startAngle) * rx + cx;
            y0 = mathSin(startAngle) * ry + cy;
          }

          bbox.fromArc(cx, cy, rx, ry, startAngle, endAngle, anticlockwise, min2, max2);
          xi = mathCos(endAngle) * rx + cx;
          yi = mathSin(endAngle) * ry + cy;
          break;

        case CMD.R:
          x0 = xi = data[i++];
          y0 = yi = data[i++];
          width = data[i++];
          height = data[i++]; // Use fromLine

          bbox.fromLine(x0, y0, x0 + width, y0 + height, min2, max2);
          break;

        case CMD.Z:
          xi = x0;
          yi = y0;
          break;
      } // Union


      vectorUtil.min(min1, min1, min2);
      vectorUtil.max(max1, max1, max2);
    } // No data


    if (i === 0) {
      min1[0] = min1[1] = max1[0] = max1[1] = 0;
    }

    return new BoundingRect(min1[0], min1[1], max1[0], max1[1], max1[0] - min1[0], max1[1] - min1[1]);
  },

<span id='qrenderer-core-PathProxy-method-rebuildPath'>  /**
</span>   * @method rebuildPath
   * Rebuild path from current data
   * Rebuild path will not consider javascript implemented line dash.
   * @param {CanvasRenderingContext2D} ctx
   */
  rebuildPath: function rebuildPath(ctx) {
    var d = this.data;
    var x0;
    var y0;
    var xi;
    var yi;
    var x;
    var y;
    var ux = this._ux;
    var uy = this._uy;
    var len = this._len;
    var cx = 0;
    var cy = 0;
    var rx = 0;
    var ry = 0;
    var theta = 0;
    var dTheta = 0;
    var psi = 0;
    var fs = 0;
    var r = 0;
    var scaleX = 0;
    var scaleY = 0;
    var isEllipse = 0;
    var endAngle = 0;

    for (var i = 0; i &lt; len;) {
      var cmd = d[i++];

      if (i === 1) {
        // 如果第一个命令是 L, C, Q
        // 则 previous point 同绘制命令的第一个 point
        //
        // 第一个命令为 Arc 的情况下会在后面特殊处理
        xi = d[i];
        yi = d[i + 1];
        x0 = xi;
        y0 = yi;
      }

      switch (cmd) {
        case CMD.M:
          x0 = xi = d[i++];
          y0 = yi = d[i++];
          ctx.moveTo(xi, yi);
          break;

        case CMD.L:
          x = d[i++];
          y = d[i++]; // Not draw too small seg between

          if (mathAbs(x - xi) &gt; ux || mathAbs(y - yi) &gt; uy || i === len - 1) {
            ctx.lineTo(x, y);
            xi = x;
            yi = y;
          }

          break;

        case CMD.C:
          ctx.bezierCurveTo(d[i++], d[i++], d[i++], d[i++], d[i++], d[i++]);
          xi = d[i - 2];
          yi = d[i - 1];
          break;

        case CMD.Q:
          ctx.quadraticCurveTo(d[i++], d[i++], d[i++], d[i++]);
          xi = d[i - 2];
          yi = d[i - 1];
          break;

        case CMD.A:
          cx = d[i++];
          cy = d[i++];
          rx = d[i++];
          ry = d[i++];
          theta = d[i++];
          dTheta = d[i++];
          psi = d[i++];
          fs = d[i++];
          r = rx &gt; ry ? rx : ry;
          scaleX = rx &gt; ry ? 1 : rx / ry;
          scaleY = rx &gt; ry ? ry / rx : 1;
          isEllipse = mathAbs(rx - ry) &gt; 1e-3;
          endAngle = theta + dTheta;

          if (isEllipse) {
            ctx.translate(cx, cy);
            ctx.rotate(psi);
            ctx.scale(scaleX, scaleY);
            ctx.arc(0, 0, r, theta, endAngle, 1 - fs);
            ctx.scale(1 / scaleX, 1 / scaleY);
            ctx.rotate(-psi);
            ctx.translate(-cx, -cy);
          } else {
            ctx.arc(cx, cy, r, theta, endAngle, 1 - fs);
          }

          if (i === 1) {
            // 直接使用 arc 命令
            // 第一个命令起点还未定义
            x0 = mathCos(theta) * rx + cx;
            y0 = mathSin(theta) * ry + cy;
          }

          xi = mathCos(endAngle) * rx + cx;
          yi = mathSin(endAngle) * ry + cy;
          break;

        case CMD.R:
          x0 = xi = d[i];
          y0 = yi = d[i + 1];
          ctx.rect(d[i++], d[i++], d[i++], d[i++]);
          break;

        case CMD.Z:
          ctx.closePath();
          xi = x0;
          yi = y0;
      }
    }
  }
};
PathProxy.CMD = CMD;
var _default = PathProxy;
module.exports = _default;</pre>
</body>
</html>
